package com.mfz.common.servlet

import com.mfz.common.definition.organization.User
import com.mfz.common.definition.organization.getUserById
import java.nio.ByteBuffer
import java.security.MessageDigest
import java.time.Duration
import java.util.*


/**
 * 这里对超时时间进行设置的地方,当前是一个小时。后续需要使用配置文件进行设置
 * 单位为分钟
 */
var tokenOvertime = Duration.ofMinutes(60).toMillis()
private const val baseNumber = 0x7f

/**
 * 一个自己设计的token。
 *
 * 格式： userId%20到期时间 并进行混淆
 * create by 2019-06-21.
 * @author yimin
 */
class HttpToken {
	val user: User?
	/**
	 * 此值表明此Token是否是有效的.有两种情况使期失效。
	 * 1.token值在前台被修改过
	 * 2.token内指定的UserId无法被定位和被实例化出来。
	 * 这两种情况下，需要重新进行登录操作
	 *
	 * 注：token是否过期并不影响此值
	 */
	val isValid: Boolean
	/**
	 * token开始的时间
	 */
	private val loseEffectivenessTime: Long

	/**
	 * 使用token值得到一个Token对象。此构造函数通常用来在请求时直接生成对象
	 */
	constructor(token: String) {
		var isValid = true
		val arr = Base64.getDecoder().decode(token)

		for (i in 0..15) {
			arr[i] = (arr[i] - baseNumber - i).toByte()
		}

		//重新签名，然后检验签名是否正确
		val md = MessageDigest.getInstance("MD5")
		md.update(arr, 0, 16)
		val dig = md.digest()

		for (i in dig.indices) {
			if (arr[16 + i] != dig[i]) {
				isValid = false
				break
			}
		}

		//得到user,并且判断user对应的用户是否存在并有效
		user = getUserById(ByteBuffer.wrap(arr, 0, 8).int)
		if (user == null)
			this.isValid = false
		else
			this.isValid = isValid

		//得到失效时间的Long值
		loseEffectivenessTime = ByteBuffer.wrap(arr, 8, 8).long
	}

	/**
	 * 通过User得到一个Token对象。此方式通常用在请求的返回上面
	 */
	constructor(user: User?) {
		this.user = user
		this.loseEffectivenessTime = System.currentTimeMillis() + tokenOvertime
		this.isValid = true
	}

	/**
	 * 检查Token是否有效或者过期。
	 */
	fun checkTokenEffective(): Boolean {
		return loseEffectivenessTime > System.currentTimeMillis()
	}

	/**
	 * 产生一个token并进行混淆
	 *
	 * 如果用户不存在，直接返回空
	 */
	fun createToken(): String? {
		//当前用户未指定时，返回一个空出去
		if (this.user == null) {
			return null
		}

		val buffer = ByteBuffer.allocate(32)

		buffer.putLong(0, user.id.toLong())
		buffer.putLong(8, System.currentTimeMillis() + tokenOvertime)

		//进行签名
		val arr = buffer.array()
		val md = MessageDigest.getInstance("MD5")
		md.update(arr, 0, 16)
		val dig = md.digest()

		for (i in 16..31) buffer.put(i, dig[i - 16])
		//进行混淆
		for (i in 0..15) buffer.put(i, (arr[i] + baseNumber + i).toByte())

		//转成Base64码。
		return Base64.getEncoder().encodeToString(buffer.array())
	}
}
